package com.jboltai.util.http;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.jboltai.event.Event;
import com.jboltai.util.share.AIEventThreadShareData;
import okhttp3.*;
import okhttp3.sse.EventSource;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class RequestInstance {


    /**
     * 使用哪个client发起的
     */
    @JSONField(serialize = false)
    private OkHttpClient client;

    @JSONField(serialize = false)
    private Call callable;

    @JSONField(serialize = false)
    private EventSource eventSource;

    /**
     * 触发请求的事件
     */
    @JSONField(serialize = false)
    private Event triggerEvent;

    @JSONField(serialize = false)
    private Request request;

    @JSONField(serialize = false)
    private HttpMethod method;

    private String url;

    @JSONField(serialize = false)
    private RequestBody body;

    /**
     * query参数
     */
    private List<Object[]> queryParam;

    private JSONObject jsonParam;
    private List<Object[]> formParam;

    private Map<String, String> headers;



    public RequestInstance(OkHttpClient client, String url, HttpMethod method) {
        if (client == null || StrUtil.isBlank(url)) {
            throw new IllegalArgumentException("参数异常");
        }

        this.client = client;

        this.url = url;

        this.method = method;

        this.headers = new HashMap<>();
    }
    public RequestInstance(OkHttpClient client, String url, HttpMethod method, Event event) {
        if (client == null || StrUtil.isBlank(url)) {
            throw new IllegalArgumentException("参数异常");
        }

        this.client = client;

        this.triggerEvent = event;

        this.url = url;

        this.method = method;

        this.headers = new HashMap<>();
    }
    /**
     * 添加url查询参数 批量
     * @param params
     * @return
     */
    public RequestInstance addAllQueryParam(Map<String,Object> params) {
        if (params == null || params.isEmpty()) {
            throw new IllegalArgumentException("参数异常，params不能为null");
        }
        if (queryParam == null) {
            queryParam = new ArrayList<>();
        }
        queryParam.addAll(params.entrySet().stream().map(o->new Object[]{o.getKey(),o.getValue()}).collect(Collectors.toList()));
        return this;
    }

    /**
     * 添加url查询参数
     * @param key
     * @param value
     * @return
     */
    public RequestInstance addQueryParam(String key, Object value) {
        if (StrUtil.isBlank(key)) {
            throw new IllegalArgumentException("参数异常，key不能为null");
        }
        if (queryParam == null) {
            queryParam = new ArrayList<>();
        }
        queryParam.add(new Object[]{key, value});
        return this;
    }

    /**
     * 添加url查询参数,必须按照key,value,key,value..的格式成对传入
     * @param keyValues
     * @return
     */
    public RequestInstance addQueryParams(Object... keyValues) {
        if (keyValues == null) {
            return this;
        }
        if (queryParam == null) {
            queryParam = new ArrayList<>();
        }
        if (keyValues.length % 2 != 0) {
            throw new IllegalArgumentException("参数数量异常，必须按照key,value,key,value..的格式成对传入");
        }
        for (int i = 0; i < keyValues.length; i+=2) {
            if (keyValues[i] == null || (keyValues[i] instanceof String) == false || StrUtil.isBlank((String)keyValues[i])) {
                throw new IllegalArgumentException("传入的key必须为string类型且值有效");
            }
            queryParam.add(new Object[]{keyValues[i], keyValues[i+1]});
        }

        return this;
    }
    /**
     * 添加application/json 请求体参数
     * @param key
     * @param value
     * @return
     */
    public RequestInstance addJsonParam(String key, Object value) {
        if (formParam != null) {
            throw new RuntimeException("请求已绑定form数据，无法再绑定json数据");
        }
        if (StrUtil.isBlank(key)) {
            throw new IllegalArgumentException("参数异常，key不能为null");
        }
        if (jsonParam == null) {
            jsonParam = new JSONObject();
        }
        jsonParam.put(key, value);
        return this;
    }
    /**
     * 添加application/json 请求体参数, 批量
     * @param params
     * @return
     */
    public RequestInstance addAllJsonParam(Map<String,Object> params) {
        if (formParam != null) {
            throw new RuntimeException("请求已绑定form数据，无法再绑定json数据");
        }
        if (params == null || params.isEmpty()) {
            return this;
        }
        if (jsonParam == null) {
            jsonParam = new JSONObject();
        }
       jsonParam.putAll(params);
        return this;
    }
    /**
     * 添加application/json 请求体参数,必须按照key,value,key,value..的格式成对传入
     * @param keyValues
     * @return
     */
    public RequestInstance addJsonParam(Object... keyValues) {
        if (formParam != null) {
            throw new RuntimeException("请求已绑定form数据，无法再绑定json数据");
        }
        if (keyValues == null) {
            return this;
        }
        if (jsonParam == null) {
            jsonParam = new JSONObject();
        }
        if (keyValues.length % 2 != 0) {
            throw new IllegalArgumentException("参数数量异常，必须按照key,value,key,value..的格式成对传入");
        }
        for (int i = 0; i < keyValues.length; i+=2) {
            if (keyValues[i] == null || (keyValues[i] instanceof String) == false || StrUtil.isBlank((String)keyValues[i])) {
                throw new IllegalArgumentException("传入的key必须为string类型且值有效");
            }
            jsonParam.put((String)keyValues[i], keyValues[i+1]);
        }

        return this;
    }
    /**
     * 添加application/x-www-form-urlencoded 请求体参数
     * @param key
     * @param value
     * @return
     */
    public RequestInstance addFormParam(String key, Object value) {
        if (jsonParam != null) {
            throw new RuntimeException("请求已绑定form数据，无法再绑定json数据");
        }
        if (StrUtil.isBlank(key)) {
            throw new IllegalArgumentException("参数异常，key不能为null");
        }
        if (formParam == null) {
            formParam = new ArrayList<>();
        }
        formParam.add(new Object[]{key, value});
        return this;
    }

    /**
     * 添加application/x-www-form-urlencoded 请求体参数,必须按照key,value,key,value..的格式成对传入
     * @param keyValues
     * @return
     */
    public RequestInstance addFormParam(Object... keyValues) {
        if (jsonParam != null) {
            throw new RuntimeException("请求已绑定form数据，无法再绑定json数据");
        }
        if (keyValues == null) {
            return this;
        }
        if (formParam == null) {
            formParam = new ArrayList<>();
        }
        if (keyValues.length % 2 != 0) {
            throw new IllegalArgumentException("参数数量异常，必须按照key,value,key,value..的格式成对传入");
        }
        for (int i = 0; i < keyValues.length; i+=2) {
            if (keyValues[i] == null || (keyValues[i] instanceof String) == false || StrUtil.isBlank((String)keyValues[i])) {
                throw new IllegalArgumentException("传入的key必须为string类型且值有效");
            }
            formParam.add(new Object[]{keyValues[i], keyValues[i+1]});
        }

        return this;
    }

    /**
     * 添加请求header
     * @param key
     * @param value
     * @return
     */
    public RequestInstance addHeader(String key, String value) {
        if (StrUtil.isBlank(key) || StrUtil.isBlank(value)) {
            throw new IllegalArgumentException("header的参数必须有效，请检查");
        }
        headers.put(key, value);
        return this;
    }

    /**
     * 添加请求header
     * @param keyValues
     * @return
     */
    public RequestInstance addHeader(String... keyValues) {
        if (keyValues == null) {
            return this;
        }
        if (keyValues.length % 2 != 0) {
            throw new IllegalArgumentException("参数数量异常，必须按照key,value,key,value..的格式成对传入");
        }
        for (int i = 0; i < keyValues.length; i+=2) {
            if (StrUtil.isBlank((String)keyValues[i]) || StrUtil.isBlank((String)keyValues[i+1]) ) {
                throw new IllegalArgumentException("传入的key value必须为string类型且值有效");
            }
            headers.put(keyValues[i], keyValues[i+1]);
        }

        return this;
    }

    /**
     * 设置header
     * @param headers
     * @return
     */
    public RequestInstance setHeaders(Map<String,String> headers) {
        this.headers = headers;
        return this;
    }

    /**
     * 设置请求头的Authorization，会自动加上Bearer 前缀
     * @param auth
     * @return
     */
    public RequestInstance setAuthorization(String auth) {

        headers.put("Authorization", "Bearer " + (auth == null ? "" : auth));
        return this;
    }

    /**
     * 设置header的Content-Type: application/json
     * @return
     */
    public RequestInstance setContentTypeToJson() {
        headers.put("Content-Type", "application/json; charset=utf-8");
        return this;
    }

    /**
     * 设置header的Content-Type: application/x-www-form-urlencoded
     * @return
     */
    public RequestInstance setContentTypeToForm() {
        headers.put("Content-Type", "application/x-www-form-urlencoded");
        return this;
    }

    public RequestInstance setBody(String body, String contentType) {
        this.body = RequestBody.create(
                body,
                MediaType.parse(contentType == null ? "text/plain" : contentType)
        );
        return this;
    }



    private void buildRequest() {
        Request.Builder builder = new Request.Builder();
        if (queryParam != null) {
            HttpUrl.Builder urlBuilder = HttpUrl.get(this.url).newBuilder();
            queryParam.forEach(item -> {
                urlBuilder.addQueryParameter((String)item[0], item[1] == null ? "" : item[1].toString());
            });
            builder.url(urlBuilder.build());
        } else {
            builder.url(this.url);
        }
        if(headers == null){
            headers = new HashMap<>();
        }

        if (body != null) {

        }
        //先处理参数，过程中根据参数自动设置content-type
        else if (formParam != null) {
            //处理form数据
            FormBody.Builder bodyBuilder = new FormBody.Builder();
            formParam.forEach(item -> {
                bodyBuilder.add((String)item[0], item[1] == null ? "" : item[1].toString());
            });
            if (!headers.containsKey("content-type") || !headers.containsKey("Content-Type")) {
                this.setContentTypeToForm();
            }
            body = bodyBuilder.build();

        } else  if (jsonParam != null) {
            //处理json数据
            body = RequestBody.create(
                    jsonParam.toJSONString(),
                    MediaType.parse("application/json")
            );
            if (!headers.containsKey("content-type") || !headers.containsKey("Content-Type")) {
                this.setContentTypeToJson();
            }
        }

        //处理请求头
        if (!headers.isEmpty()) {
            headers.entrySet().iterator().forEachRemaining(entry -> {
                builder.addHeader(entry.getKey(), entry.getValue());
            });
        }

        switch (method) {
            case GET:
                request = builder.get().build();
                break;
            case POST:
                request = builder.post(body).build();
                break;
        }


    }


    /**
     * 发送同步请求
     * @return
     * @throws IOException
     */
    public ResponseBody send() throws IOException {
        if (request != null) {
            throw new RuntimeException("请求已发送，不可重复发起");
        }
        buildRequest();
        this.callable = client.newCall(request);
        Response response = this.callable.execute();
        return response.body();
    }
    /**
     * 发送同步请求
     * @return
     * @throws IOException
     */
    public Response sendForResponse() throws IOException {
        if (request != null) {
            throw new RuntimeException("请求已发送，不可重复发起");
        }
        buildRequest();
        this.callable = client.newCall(request);
        return this.callable.execute();
    }

    /**
     * 发送异步请求。<br/>
     * <b>注意：</b>如果successHandler另起了一个线程处理，可能导致response无法正常关闭，造成资源泄露。这时候需要手动调用ResponseBody.close关闭资源
     * @return
     */
    public void asyncSend(Consumer<Response> successHandler, Consumer<Throwable> failHandler ) {
        if (request != null) {
            throw new RuntimeException("请求已发送，不可重复发起");
        }
        buildRequest();

        this.callable = client.newCall(request);

        final Event triggerEvent = this.triggerEvent;

        this.callable.enqueue(new Callback() {

            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                if (triggerEvent != null && triggerEvent.getThreadShareData() != null) {
                    AIEventThreadShareData.setShareData(triggerEvent.getThreadShareData());
                }
                if (failHandler != null) {
                    failHandler.accept(e);
                }
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                try (Response r = response) {
                    if (triggerEvent != null && triggerEvent.getThreadShareData() != null) {
                        AIEventThreadShareData.setShareData(triggerEvent.getThreadShareData());
                    }
                    if (successHandler != null) {
                        successHandler.accept(r);
                    }
                }

            }
        });

    }

    /**
     * 异步处理 sse响应
     */
    public void asyncSendWithSSE(Consumer<String> successHandler, BiConsumer<Response, Throwable> failHandler ) {
        if (request != null) {
            throw new RuntimeException("请求已发送，不可重复发起");
        }
        buildRequest();
        this.eventSource = HttpUtil.getSSEFactory(client).newEventSource(request, new SSEListener(this.triggerEvent, successHandler, failHandler));
    }

    public JSONObject getJsonParam() {
        return jsonParam;
    }

    /**
     * 取消请求
     */
    public void cancal() {
        if (this.callable != null) {
            this.callable.cancel();
        }
        if (this.eventSource != null) {
            this.eventSource.cancel();
        }
    }


}
